#include "core/main.h"

namespace DDRConstants
{
    static const int arrowSize = 45;
    static const int boardWidth = 4 * arrowSize;
    static const int boardHeight = Constants::NESHeight;

    static const int beatLength = 5;
    static const int arrowSpeed = 9;
    static const double arrowSpawnRate = 0.25;

    static const int maxFrames = 1000;

    static const int arrowReward = 1000;
}

void GameDDR::Init()
{
    _arrowSprites[0] = _collection.AddSprite(Bitmap::LoadFile("../assets/ddr/arrowLeft.png"), "arrowLeft");
    _arrowSprites[1] = _collection.AddSprite(Bitmap::LoadFile("../assets/ddr/arrowDown.png"), "arrowUp");
    _arrowSprites[2] = _collection.AddSprite(Bitmap::LoadFile("../assets/ddr/arrowUp.png"), "arrowDown");
    _arrowSprites[3] = _collection.AddSprite(Bitmap::LoadFile("../assets/ddr/arrowRight.png"), "arrowRight");

    _arrowBaseSprites[0] = _collection.AddSprite(Bitmap::LoadFile("../assets/ddr/arrowBaseLeft.png"), "arrowBaseLeft");
    _arrowBaseSprites[1] = _collection.AddSprite(Bitmap::LoadFile("../assets/ddr/arrowBaseDown.png"), "arrowBaseDown");
    _arrowBaseSprites[2] = _collection.AddSprite(Bitmap::LoadFile("../assets/ddr/arrowBaseUp.png"), "arrowBaseUp");
    _arrowBaseSprites[3] = _collection.AddSprite(Bitmap::LoadFile("../assets/ddr/arrowBaseRight.png"), "arrowBaseRight");
    
    _collection.AddFontSprites();

    _observedIntNames.push_back("Reset");
    _observedIntNames.push_back("Score");
    _observedIntNames.push_back("Frame score");
    _observedIntNames.push_back("dummy");
    _observedIntNames.push_back("dummy");
    _observedIntNames.push_back("Arrows hit");
    _observedIntNames.push_back("Arrows missed");

    _activeFrame.observedInts.resize(ObservedIntNames().size());
}

void GameDDR::NewGame()
{
    _state.arrowPositions.clear();
    _state.arrowPositions.resize(4);
    
    _state.frameNum = 0;
    _state.score = 0;
    _state.arrowsHit = 0;
    _state.arrowsMissed = 0;
}

void GameDDR::Step(ControllerState &controller)
{
    _state.frameNum++;

    int frameScore = 0;

    // Check for arrow hits
    if (controller.left() && !_state.arrowPositions[0].empty() && _state.arrowPositions[0][0] == 0) {
        _state.arrowPositions[0].pop_front();
        frameScore += DDRConstants::arrowReward;
        _state.arrowsHit++;
    }
    if (controller.down() && !_state.arrowPositions[1].empty() && _state.arrowPositions[1][0] == 0) {
        _state.arrowPositions[1].pop_front();
        frameScore += DDRConstants::arrowReward;
        _state.arrowsHit++;
    }
    if (controller.up() && !_state.arrowPositions[2].empty() && _state.arrowPositions[2][0] == 0) {
        _state.arrowPositions[2].pop_front();
        frameScore += DDRConstants::arrowReward;
        _state.arrowsHit++;
    }
    if (controller.right() && !_state.arrowPositions[3].empty() && _state.arrowPositions[3][0] == 0) {
        _state.arrowPositions[3].pop_front();
        frameScore += DDRConstants::arrowReward;
        _state.arrowsHit++;
    }

    // Update all arrows
    for (UINT i = 0; i < 4; i++) {
        for (UINT j = 0; j < _state.arrowPositions[i].size(); j++) {
            _state.arrowPositions[i][j] -= DDRConstants::arrowSpeed;
        }
        // Remove arrows that have gone off the screen
        auto it = _state.arrowPositions[i].begin();
        while (it != _state.arrowPositions[i].end()) {
            if (*it < -DDRConstants::arrowSize) {
                it = _state.arrowPositions[i].erase(it);
                _state.arrowsMissed++;
            } else {
                ++it;
            }
        }
    }

    // Maybe spawn arrows
    if (_state.frameNum % DDRConstants::beatLength == 0 && rnd() < DDRConstants::arrowSpawnRate) {
        int index = rand() % 4;
        int y = DDRConstants::arrowSize * (DDRConstants::boardHeight / DDRConstants::arrowSize);
        _state.arrowPositions[index].push_back(y);
    }

    _state.score += frameScore / 100;

    bool reset = _state.frameNum == DDRConstants::maxFrames;

    stringstream ss;
    ss << _state.frameNum;
    mike::GlTextConsolePrint("frameNum", ss.str());

    _activeFrame.observedInts[0] = reset ? 1 : 0;
    _activeFrame.observedInts[1] = _state.score;
    _activeFrame.observedInts[2] = frameScore;
    _activeFrame.observedInts[3] = 0;
    _activeFrame.observedInts[4] = 0;
    _activeFrame.observedInts[5] = _state.arrowsHit;
    _activeFrame.observedInts[6] = _state.arrowsMissed;
   
    if (reset) NewGame();

    MakeFrameSprites();
}

ControllerState GameDDR::SimpleAI()
{
    ControllerState state;

    return state;
}

void GameDDR::MakeFrameSprites()
{
    _activeFrame.sprites.clear();

    // Draw the arrow base sprites
    for (int i = 0; i < 4; i++) {
        int y = 0;
        int x = i * DDRConstants::arrowSize;
        if (y >= 0) {
            _activeFrame.sprites.push_back(SpriteLocation(Vec2i(x, y), _arrowBaseSprites[i], 1));
        }
    }

    // Draw the arrow sprites
    for (int i = 0; i < 4; i++) {
        for (UINT j = 0; j < _state.arrowPositions[i].size(); j++) {
            int y = _state.arrowPositions[i][j];
            int x = i * DDRConstants::arrowSize;
            if (y >= 0) {
                _activeFrame.sprites.push_back(SpriteLocation(Vec2i(x, y), _arrowSprites[i], 1));
            }
        }
    }

    // Draw the score
    _collection.DrawText("SCORE", Vec2i(25, 3), _activeFrame.sprites, 0);
    _collection.DrawNumber(_state.score, 6, Vec2i(25, 4), _activeFrame.sprites, 0);
}